1# Attachments Tutorial
   2
   3<!-- TOC -->
   4
   5## Attachables
   6BOSL2 introduces the concept of attachables.  You can do the following
   7things with attachable shapes:
   8
   9* Control where the shape appears and how it is oriented by anchoring and specifying orientation and spin
  10* Position or attach shapes relative to parent objects
  11* Tag objects and then control boolean operations based on their tags.
  12* Change the color of objects so that child objects are different colors than their parents
  13
  14The various attachment features may seem complex at first, but 
  15attachability is one of the most important features of the BOSL2
  16library.  It enables you to position objects relative to other objects
  17in your model instead of having to keep track of absolute positions.
  18It makes models simpler, more intuitive, and easier to maintain.
  19
  20Almost all objects defined by BOSL2 are attachable.  In addition,
  21BOSL2 overrides the built-in definitions for `cube()`, `cylinder()`,
  22`sphere()`, `square()`, `circle()` and `text()` and makes them attachable as
  23well.  However, some basic OpenSCAD built-in definitions are not
  24attachable and will not work with the features described in this
  25tutorial.  The non-attachables are `polyhedron()`, `linear_extrude()`,
  26`rotate_extrude()`, `surface()`, `projection()` and `polygon()`.
  27Some of these have attachable alternatives: `vnf_polyhedron()`,
  28`linear_sweep()`, `rotate_sweep()`, and `region()`.  
  29
  30
  31## Anchoring
  32Anchoring allows you to align a specified part of an object or point
  33on an object with the origin.  The alignment point can be the center
  34of a side, the center of an edge, a corner, or some other
  35distinguished point on the object.  This is done by passing a vector
  36or text string into the `anchor=` argument.  For roughly cubical
  37or prismoidal shapes, that vector points in the general direction of the side, edge, or
  38corner that will be aligned to.  For example, a vector of [1,0,-1] refers to the lower-right
  39edge of the shape.  Each vector component should be -1, 0, or 1:
  40
  41```openscad-3D
  42include <BOSL2/std.scad>
  43// Anchor at upper-front-left corner
  44cube([40,30,50], anchor=[-1,-1,1]);
  45```
  46
  47```openscad-3D
  48include <BOSL2/std.scad>
  49// Anchor at upper-right edge
  50cube([40,30,50], anchor=[1,0,1]);
  51```
  52
  53```openscad-3D
  54include <BOSL2/std.scad>
  55// Anchor at bottom face
  56cube([40,30,50], anchor=[0,0,-1]);
  57```
  58
  59Since manually written vectors are not very intuitive, BOSL2 defines some standard directional
  60vector constants that can be added together:
  61
  62Constant | Direction | Value
  63-------- | --------- | -----------
  64`LEFT`   | X-        | `[-1, 0, 0]`
  65`RIGHT`  | X+        | `[ 1, 0, 0]`
  66`FRONT`/`FORWARD`/`FWD` | Y− | `[ 0, −1, 0]`
  67`BACK`   | Y+        | `[ 0, 1, 0]`
  68`BOTTOM`/`BOT`/`DOWN` | Z− (Y− in 2D) | `[ 0, 0, −1]` (`[0, −1]` in 2D.)
  69`TOP`/`UP` | Z+ (Y+ in 2D)      | `[ 0, 0, 1]` (`[0, 1]` in 2D.)
  70`CENTER`/`CTR` | Centered | `[ 0, 0, 0]`
  71
  72If you want a vector pointing towards the bottom−left edge, just add the `BOTTOM` and `LEFT` vector
  73constants together like `BOTTOM + LEFT`.  This will result in a vector of `[−1,0,−1]`.  You can pass
  74that to the `anchor=` argument for a clearly understandable anchoring:  
  75
  76```openscad-3D
  77include <BOSL2/std.scad>
  78cube([40,30,50], anchor=BACK+TOP);
  79```
  80
  81```openscad-3D
  82include <BOSL2/std.scad>
  83cube([40,30,50], anchor=FRONT);
  84```
  85
  86---
  87
  88For cylindrical type attachables, the Z component of the vector will be −1, 0, or 1, referring
  89to the bottom rim, the middle side, or the top rim of the cylindrical or conical shape.
  90The X and Y components can be any value, pointing towards the circular perimeter of the cone.
  91These combined let you point at any place on the bottom or top rims, or at an arbitrary
  92side wall. 
  93
  94```openscad-3D
  95include <BOSL2/std.scad>
  96cylinder(r1=25, r2=15, h=60, anchor=TOP+LEFT);
  97```
  98
  99```openscad-3D
 100include <BOSL2/std.scad>
 101cylinder(r1=25, r2=15, h=60, anchor=BOTTOM+FRONT);
 102```
 103
 104Here we convert a 30 deg angle into an anchor using [cylindrical_to_xyz()](https://github.com/BelfrySCAD/BOSL2/wiki/coords.scad#function-cylindrical_to_xyz)
 105
 106```openscad-3D
 107include <BOSL2/std.scad>
 108cylinder(r1=25, r2=15, h=60, anchor=cylindrical_to_xyz(1,30,1));
 109```
 110
 111---
 112
 113For Spherical type attachables, you can pass a vector that points at any arbitrary place on
 114the surface of the sphere:
 115
 116```openscad-3D
 117include <BOSL2/std.scad>
 118sphere(r=50, anchor=TOP);
 119```
 120
 121```openscad-3D
 122include <BOSL2/std.scad>
 123sphere(r=50, anchor=TOP+FRONT);
 124```
 125
 126Here the [spherical_to_xyz()](https://github.com/BelfrySCAD/BOSL2/wiki/coords.scad#function-spherical_to_xyz) function converts spherical coordinates into
 127a vector you can use as an anchor:
 128
 129```openscad-3D
 130include <BOSL2/std.scad>
 131sphere(r=50, anchor=spherical_to_xyz(1,-30,60));
 132```
 133
 134---
 135
 136Some attachable shapes may provide specific named anchors for shape-specific anchoring.  These
 137will be given as strings and will be specific to that type of
 138attachable.  When named anchors are supported, they are listed in a
 139"Named Anchors" section of the documentation for the module.  The
 140`teardrop()` attachable, for example, has a named anchor called "cap" and in 2D the
 141`star()` attachable has anchors labeled by tip number: 
 142
 143```openscad-3D
 144include <BOSL2/std.scad>
 145teardrop(d=100, l=20, anchor="cap");
 146```
 147
 148```openscad-2D
 149include <BOSL2/std.scad>
 150star(n=7, od=30, id=20, anchor="tip2");
 151```
 152
 153---
 154
 155Some shapes, for backwards compatibility reasons, can take a `center=` argument.  This just
 156overrides the `anchor=` argument.  A `center=true` argument is the same as `anchor=CENTER`.
 157A `center=false` argument chooses the anchor to match the behavior of
 158the builtin version:  for a cube it is the same as `anchor=[-1,-1,-1]` but for a
 159cylinder, it is the same as `anchor=BOTTOM`.
 160
 161```openscad-3D
 162include <BOSL2/std.scad>
 163cube([50,40,30],center=true);
 164```
 165
 166```openscad-3D
 167include <BOSL2/std.scad>
 168cube([50,40,30],center=false);
 169```
 170
 171---
 172
 173Most 2D shapes provided by BOSL2 are also anchorable.  The built-in `square()` and `circle()`
 174modules have been overridden to make them attachable..  The `anchor=` options for 2D
 175shapes treat 2D vectors as expected.  Special handling occurs with 3D
 176vectors:  if the Y coordinate is zero and the Z coordinate is nonzero,
 177then the Z coordinate is used to replace the Y coordinate.  This is
 178done so that you can use the TOP and BOTTOM names as anchor for 2D
 179shapes.  
 180
 181
 182```openscad-2D
 183include <BOSL2/std.scad>
 184square([40,30], anchor=BACK+LEFT);
 185```
 186
 187```openscad-2D
 188include <BOSL2/std.scad>
 189circle(d=50, anchor=BACK);
 190```
 191
 192```openscad-2D
 193include <BOSL2/std.scad>
 194hexagon(d=50, anchor=LEFT);
 195```
 196
 197```openscad-2D
 198include <BOSL2/std.scad>
 199ellipse(d=[50,30], anchor=FRONT);
 200
 201This final 2D example shows using the 3D anchor, TOP, with a 2D
 202object.  Also notice how the pentagon anchors to its most extreme point on
 203the Y+ axis.  
 204
 205```openscad-2D
 206include <BOSL2/std.scad>
 207pentagon(d=50, anchor=TOP);
 208```
 209
 210
 211## Spin
 212You can spin attachable objects around the origin using the `spin=`
 213argument.  The spin applies **after** anchoring, so depending on how
 214you anchor an object, its spin may not be about its center.  This
 215means that spin can have an effect even on rotationally symmetric
 216objects like spheres and cylinders.  You specify the spin in degrees.
 217A positive number will result in a counter-clockwise spin around the Z
 218axis (as seen from above), and a negative number will make a clockwise
 219spin:
 220
 221```openscad-3D
 222include <BOSL2/std.scad>
 223cube([20,20,40], center=true, spin=45);
 224```
 225
 226You can also spin around other axes, or multiple axes at once, by giving 3 angles (in degrees) to
 227`spin=` as a vector, like [Xang,Yang,Zang].  Similarly to `rotate()`,
 228the rotations apply in the order given, X-axis spin, then Y-axis, then Z-axis:
 229
 230```openscad-3D
 231include <BOSL2/std.scad>
 232cube([20,20,40], center=true, spin=[10,20,30]);
 233```
 234
 235This example shows a cylinder which has been anchored at its FRONT,
 236with a rotated copy in gray.  The rotation is performed around the
 237origin, but the cylinder is off the origin, so the rotation **does**
 238have an effect on the cylinder, even though the cylinder has
 239rotational symmetry.
 240
 241```openscad-3D
 242include <BOSL2/std.scad>
 243cylinder(h=40,d=20,anchor=FRONT+BOT);
 244%cylinder(h=40.2,d=20,anchor=FRONT+BOT,spin=40);
 245```
 246
 247
 248
 249You can also apply spin to 2D shapes from BOSL2, though only by scalar angle:
 250
 251```openscad-2D
 252include <BOSL2/std.scad>
 253square([40,30], spin=30);
 254```
 255
 256```openscad-2D
 257include <BOSL2/std.scad>
 258ellipse(d=[40,30], spin=30);
 259```
 260
 261
 262## Orientation
 263Another way to specify a rotation for an attachable shape, is to pass a 3D vector via the
 264`orient=` argument.  This lets you specify what direction to tilt the top of the shape towards.
 265For example, you can make a cone that is tilted up and to the right like this:
 266
 267```openscad-3D
 268include <BOSL2/std.scad>
 269cylinder(h=100, r1=50, r2=20, orient=UP+RIGHT);
 270```
 271
 272More precisely, the Z direction of the shape is rotated to align with
 273the vector you specify.  Two dimensional attachables, which have no Z vector,
 274do not accept the `orient=` argument.  
 275
 276
 277## Mixing Anchoring, Spin, and Orientation
 278When giving `anchor=`, `spin=`, and `orient=`, they are applied anchoring first, spin second,
 279then orient last.  For example, here's a cube:
 280
 281```openscad-3D
 282include <BOSL2/std.scad>
 283cube([20,20,50]);
 284```
 285
 286You can center it with an `anchor=CENTER` argument:
 287
 288```openscad-3D
 289include <BOSL2/std.scad>
 290cube([20,20,50], anchor=CENTER);
 291```
 292
 293Add a 45 degree spin:
 294
 295```openscad-3D
 296include <BOSL2/std.scad>
 297cube([20,20,50], anchor=CENTER, spin=45);
 298```
 299
 300Now tilt the top up and forward:
 301
 302```openscad-3D
 303include <BOSL2/std.scad>
 304cube([20,20,50], anchor=CENTER, spin=45, orient=UP+FWD);
 305```
 306
 307For 2D shapes, you can mix `anchor=` with `spin=`, but not with `orient=`.
 308
 309```openscad-2D
 310include <BOSL2/std.scad>
 311square([40,30], anchor=BACK+LEFT, spin=30);
 312```
 313
 314## Positioning Children
 315
 316Positioning is a powerful method for placing an object relative to
 317another object.  You do this by making the second object a child of
 318the first object.  By default, the child's anchor point will be
 319aligned with the center of the parent.  The default anchor for `cyl()`
 320is CENTER, and in this case, the cylinder is centered on the cube's center
 321
 322```openscad-3D
 323include <BOSL2/std.scad>
 324up(13) cube(50)
 325    cyl(d=25,l=95);
 326```
 327
 328With `cylinder()` the default anchor is BOTTOM.  It's hard to tell,
 329but the cylinder's bottom is placed at the center of the cube.  
 330
 331```openscad-3D
 332include <BOSL2/std.scad>
 333cube(50)
 334    cylinder(d=25,h=75);
 335```
 336
 337If you explicitly anchor the child object then the anchor you choose will be aligned
 338with the center point of the parent object.  In this example the right
 339side of the cylinder is aligned with the center of the cube.  
 340
 341
 342```openscad-3D
 343include <BOSL2/std.scad>
 344cube(50,anchor=FRONT)     
 345    cylinder(d=25,h=95,anchor=RIGHT);
 346```
 347
 348The `position()` module enables you to specify where on the parent to
 349position the child object.  You give `position()` an anchor point on
 350the parent, and the child's anchor point is aligned with the specified
 351parent anchor point.  In this example the LEFT anchor of the cylinder is positioned on the
 352RIGHT anchor of the cube.  
 353
 354```openscad-3D
 355include <BOSL2/std.scad>
 356cube(50,anchor=FRONT)     
 357    position(RIGHT) cylinder(d=25,h=75,anchor=LEFT);
 358```
 359
 360Using this mechanism you can position objects relative to other
 361objects which are in turn positioned relative to other objects without
 362having to keep track of the transformation math.
 363
 364```openscad-3D
 365include <BOSL2/std.scad>
 366cube([50,50,30],center=true)
 367    position(TOP+RIGHT) cube([25,40,10], anchor=RIGHT+BOT)
 368       position(LEFT+FRONT+TOP) cube([12,12,8], anchor=LEFT+FRONT+BOT)
 369         cylinder(h=10,r=3);
 370```
 371
 372The positioning mechanism is not magical: it simply applies a
 373`translate()` operation to the child.  You can still apply your own
 374additional translations or other transformations if you wish.  For
 375example, you can position an object 5 units from the right edge:
 376
 377```openscad-3D
 378include<BOSL2/std.scad>
 379cube([50,50,20],center=true)
 380    position(TOP+RIGHT) left(5) cube([4,50,10], anchor=RIGHT+BOT);
 381```
 382
 383
 384
 385Positioning objects works the same way in 2D.
 386
 387```openscad-2D
 388include<BOSL2/std.scad>
 389square(10)
 390    position(RIGHT) square(3,anchor=LEFT);
 391```
 392
 393## Using position() with orient()
 394
 395When positioning an object near an edge or corner you may wish to
 396orient the object relative to some face other than the TOP face that
 397meets at that edge or corner.  You can always apply `rot()` to 
 398change the orientation of the child object, but in order to do this,
 399you need to figure out the correct rotation.  The `orient()` module provides a
 400mechanism for re-orienting the child() that eases this burden: 
 401it can orient the child relative to the parent anchor directions.  This is different
 402than giving an `orient=` argument to the child, because that orients
 403relative to the parent's global coordinate system by just using the vector
 404directly, instead of orienting to the parent's anchor, which takes
 405account of face orientation.  A series of three
 406examples shows the different results.  In the first example, we use
 407only `position()`.  The child cube is erected pointing upwards, in the
 408Z direction.  In the second example we use `orient=RIGHT` in the child
 409and the result is that the child object points in the X+ direction,
 410without regard for the shape of the parent object.  In the final
 411example we apply `orient(RIGHT)` and the child is oriented
 412relative to the slanted right face of the parent using the parent
 413RIGHT anchor.   
 414
 415```openscad-3D
 416include<BOSL2/std.scad>
 417prismoid([50,50],[30,30],h=40)
 418  position(RIGHT+TOP)
 419     cube([15,15,25],anchor=RIGHT+BOT);
 420```
 421
 422
 423```openscad-3D
 424include<BOSL2/std.scad>
 425prismoid([50,50],[30,30],h=40)
 426  position(RIGHT+TOP)
 427     cube([15,15,25],orient=RIGHT,anchor=LEFT+BOT);
 428```
 429
 430
 431```openscad-3D
 432include<BOSL2/std.scad>
 433prismoid([50,50],[30,30],h=40)
 434  position(RIGHT+TOP)
 435     orient(RIGHT)
 436        cube([15,15,25],anchor=BACK+BOT);
 437```
 438
 439You may have noticed that the children in the above three examples
 440have different anchors.  Why is that?  The first and second examples
 441differ because anchoring up and anchoring to the right require
 442anchoring on opposite sides of the child.  But the third case differs
 443because the spin has changed.  The examples below show the same models
 444but with arrows replacing the child cube.  The red flags on the arrows
 445mark the zero spin direction.  Examine the red flags to see how the spin
 446changes.  The Y+ direction of the child will point towards that red
 447flag.  
 448
 449```openscad-3D
 450include<BOSL2/std.scad>
 451prismoid([50,50],[30,30],h=40)
 452  position(RIGHT+TOP)
 453     anchor_arrow(40);
 454```
 455
 456
 457```openscad-3D
 458include<BOSL2/std.scad>
 459prismoid([50,50],[30,30],h=40)
 460  position(RIGHT+TOP)
 461     anchor_arrow(40, orient=RIGHT);
 462```
 463
 464```openscad-3D
 465include<BOSL2/std.scad>
 466prismoid([50,50],[30,30],h=40)
 467  position(RIGHT+TOP)
 468     orient(RIGHT)
 469        anchor_arrow(40);
 470```
 471
 472
 473## Aligning children with align()
 474
 475You may have noticed that with position() and orient(), specifying the
 476child anchors to position objects flush with their parent can be
 477annoying, or sometimes even tricky.  You can simplify this task by
 478using the align() module.  This module positions children on faces
 479of a parent and aligns to edges or corners, while picking the correct anchor points on
 480the children so that the children line up correctly with the parent.  
 481
 482In the simplest case, if you want to place a child on the RIGHT side
 483of its parent, you need to anchor the child to its LEFT anchor:
 484
 485```openscad-3D
 486include<BOSL2/std.scad>
 487cuboid([50,40,15])
 488    position(RIGHT)
 489        color("lightblue")cuboid(5,anchor=LEFT);
 490```
 491
 492When you use align() it automatically determines the correct anchor to
 493use for the child and this anchor overrides any anchor specified to
 494the child:  any anchor you specify for the child is ignored.
 495
 496```openscad-3D
 497include<BOSL2/std.scad>
 498cuboid([50,40,15])
 499    align(RIGHT)
 500        color("lightblue")cuboid(5);
 501```
 502
 503To place the child on top of the parent in the corner you can do use
 504align as shown below instead of specifying the RIGHT+FRONT+BOT anchor
 505with position(): 
 506
 507```openscad-3D
 508include<BOSL2/std.scad>
 509cuboid([50,40,15])
 510    align(TOP,RIGHT+FRONT)
 511        color("lightblue")prismoid([10,5],[7,4],height=4);
 512```
 513
 514Both position() and align() can accept a list of anchor locations and
 515makes several copies of the children, but
 516if you want the children positioned flush, each copy 
 517requires a different anchor, so it is impossible to do this with a
 518single call to position(), but easily done using align():
 519
 520```openscad-3D
 521include<BOSL2/std.scad>
 522cuboid([50,40,15])
 523    align(TOP,[RIGHT,LEFT])
 524        color("lightblue")prismoid([10,5],[7,4],height=4);
 525```
 526
 527If you want the children close to the edge but not actually flush you
 528can use the `inset=` parameter of align to achieve this:
 529
 530```openscad-3D
 531include<BOSL2/std.scad>
 532cuboid([50,40,15])
 533    align(TOP,[FWD,RIGHT,LEFT,BACK],inset=3)
 534        color("lightblue")prismoid([10,5],[7,4],height=4);
 535```
 536
 537If you spin the children then align will still do the right thing
 538
 539```openscad-3D
 540include<BOSL2/std.scad>
 541cuboid([50,40,15])
 542    align(TOP,[RIGHT,LEFT])
 543        color("lightblue")prismoid([10,5],[7,4],height=4,spin=90);
 544```
 545
 546If you orient the object DOWN it will be attached from its top anchor,
 547correctly aligned.  
 548
 549```openscad-3D
 550include<BOSL2/std.scad>
 551cuboid([50,40,15])
 552    align(TOP,RIGHT)
 553        color("lightblue")prismoid([10,5],[7,4],height=4,orient=DOWN);
 554```
 555
 556Note that align() never changes the orientation of the children.  If
 557you put the blue prismoid on the right side the anchors line up but
 558the edges of the child and parent don't.
 559
 560```openscad-3D
 561include<BOSL2/std.scad>
 562prismoid(50,30,25){
 563  align(RIGHT,TOP)
 564    color("lightblue")prismoid([10,5],[7,4],height=4);
 565}
 566```
 567
 568If you apply spin that is not a multiple of 90 degrees then alignment
 569will line up the corner
 570
 571```openscad-3D
 572include<BOSL2/std.scad>
 573cuboid([50,40,15])
 574    align(TOP,RIGHT)
 575        color("lightblue")cuboid(8,spin=33);
 576```
 577
 578You can also attach objects to a cylinder.  If you use the usual cubic
 579anchors then a cube will attach on a face as shown here:
 580
 581```openscad-3D
 582include<BOSL2/std.scad>
 583cyl(h=20,d=10,$fn=128)
 584  align(RIGHT,TOP)
 585    color("lightblue")cuboid(5);
 586```
 587
 588But with a cylinder you can choose an arbitrary horizontal angle for
 589the anchor.  If you do this, similar to the case of arbitrary spin,
 590the cube will attach on the nearest corner.
 591
 592```openscad-3D
 593include<BOSL2/std.scad>
 594cyl(h=20,d=10,$fn=128)
 595  align([1,.3],TOP)
 596    color("lightblue")cuboid(5);
 597```
 598
 599## Attachment overview
 600
 601Attachables get their name from their ability to be attached to each
 602other.  Unlike with positioning, attaching changes the orientation of
 603the child object.  Think of it like sticking two objects together:
 604when you attach an object, it appears on the parent
 605relative to the local coordinate system of the parent at the anchor point.  To understand
 606what this means, imagine the perspective of an ant walking on a
 607sphere.  The meaning of UP varies depending on where on the sphere the
 608ant is standing.  If you **attach** a cylinder to the sphere then the cylinder will
 609be "up" from the ant's perspective.   The first example shows a
 610cylinder placed with `position()` so it points up in the global parent
 611coordinate system.  The second example shows how `attach()` points the
 612cylinder UP from the perspective of an ant standing at the anchor
 613point on the sphere.  
 614
 615```openscad-3D
 616include<BOSL2/std.scad>
 617sphere(40)
 618    position(RIGHT+TOP) cylinder(r=8,h=20);
 619```
 620
 621
 622```openscad-3D
 623include<BOSL2/std.scad>
 624sphere(40)
 625    attach(RIGHT+TOP) cylinder(r=8,h=20);
 626```
 627
 628In the example above, the cylinder's center point is attached to the
 629sphere, pointing "up" from the perspective of the sphere's surface.
 630For a sphere, a surface normal is defined everywhere that specifies
 631what "up" means.  But for other objects, it may not be so obvious.
 632Usually at edges and corners the direction is the average of the
 633direction of the faces that meet there.
 634
 635When you specify an anchor you are actually specifying both an anchor
 636point but also an anchor direction.  If you want to visualize this
 637direction you can use anchor arrows.  
 638
 639
 640## Anchor Directions and Anchor Arrows
 641For the ant on the sphere it is obvious which direction is UP; that
 642direction corresponds to the Z+ axis.  The location of the X and Y
 643axes is less clear and in fact it may be arbitrary.  One way that is
 644useful to show the position and orientation of an anchor point is by
 645attaching an anchor arrow to that anchor.  As noted before, the small
 646red flag points in the direction of the anchor's Y+ axis when the spin
 647is zero.
 648
 649```openscad-3D
 650include <BOSL2/std.scad>
 651cube(18, center=true)
 652    attach(LEFT+TOP)
 653        anchor_arrow();
 654```
 655
 656For large objects, you can change the size of the arrow with the `s=` argument.
 657
 658```openscad-3D
 659include <BOSL2/std.scad>
 660sphere(d=100)
 661    attach(LEFT+TOP)
 662        anchor_arrow(s=50);
 663```
 664
 665To show all the standard cardinal anchor points, you can use the [show_anchors()](https://github.com/BelfrySCAD/BOSL2/wiki/attachments.scad#module-show_anchors) module.
 666
 667```openscad-3D;Big
 668include <BOSL2/std.scad>
 669cube(40, center=true)
 670    show_anchors();
 671```
 672
 673```openscad-3D;Big
 674include <BOSL2/std.scad>
 675cylinder(h=40, d=40, center=true)
 676    show_anchors();
 677```
 678
 679```openscad-3D;Big
 680include <BOSL2/std.scad>
 681sphere(d=40)
 682    show_anchors();
 683```
 684
 685For large objects, you can again change the size of the arrows with the `s=` argument.
 686
 687```openscad-3D;Big
 688include <BOSL2/std.scad>
 689prismoid(150,60,100)
 690    show_anchors(s=35);
 691```
 692
 693## Parent-Child Anchor Attachment (Double Argument Attachment)
 694
 695The `attach()` module has two different modes of operation,
 696parent-child anchor attachment and parent anchor attachment.  These
 697are also called double argument attachment and single argument
 698attachment.  The parent-child anchor attachment, with two arguments,
 699is usually easier to use and is more powerful because it supports
 700alignment.  When you use parent-child anchor attachment you give a
 701parent anchor and a child anchor.  Imagine pointing the anchor arrows
 702on the two objects directly at each other and pushing them together in
 703the direction of the arrows until they touch.  In many of the examples
 704below we show first the two objects with their anchor arrows and then
 705the result of the attach operation using those anchors.
 706
 707```openscad-3D
 708include <BOSL2/std.scad>
 709cube(50,anchor=BOT) attach(TOP,BOT) anchor_arrow(30);
 710right(60)cylinder(d1=30,d2=15,h=25) attach(BOT,BOT) anchor_arrow(30);
 711```
 712
 713```openscad-3D
 714include <BOSL2/std.scad>
 715cube(50,anchor=BOT)
 716  attach(TOP,BOT) cylinder(d1=30,d2=15,h=25);
 717```
 718
 719This example produces the same result as using `align()`, but if the
 720parent anchor is not horizontal, then the child is reoriented:
 721
 722```openscad-3D
 723include <BOSL2/std.scad>
 724prismoid([50,50],[35,35],h=50,anchor=BOT) attach(RIGHT,BOT) anchor_arrow(30);
 725right(60)cylinder(d1=30,d2=15,h=25) attach(BOT,BOT) anchor_arrow(30);
 726```
 727
 728```openscad-3D
 729include <BOSL2/std.scad>
 730prismoid([50,50],[35,35],h=50,anchor=BOT)
 731  attach(RIGHT,BOT) cylinder(d1=30,d2=15,h=25);
 732```
 733
 734In this case we attach the curved side of the cone to a cube by lining
 735up the anchor arrows:
 736
 737```openscad-3D
 738include <BOSL2/std.scad>
 739cube(50,center=true) attach(RIGHT,BOT) anchor_arrow(30);
 740right(80)cylinder(d1=30,d2=15,h=25) attach(LEFT,BOT) anchor_arrow(30);
 741```
 742
 743```openscad-3D
 744include <BOSL2/std.scad>
 745cube(50,center=true)
 746  attach(RIGHT,LEFT) cylinder(d1=30,d2=15,h=25);
 747```
 748
 749Note that this form of attachent overrides any anchor or orientation
 750specified in the child: **with parent-child anchor attachment the
 751`anchor=` and `orient=` parameters to the child are ignored.**
 752
 753When you specify attachment using a pair of anchors, the attached
 754child can spin around the parent anchor while still being attached at
 755the designated anchors: specifying the anchors leaves one unspecified
 756degree of freedom.  As noted earlier, this ambiguity is resolved by anchors having a
 757defined spin which specifies where the Y+ axis is located.
 758The way that BOSL2 positions objects can be understood by viewing the
 759anchor arrows as shown above, or you can remember these rules:
 7601. When attaching to the TOP or BOTTOM: the FRONT of the child points to the front if possible;  otherwise the TOP of the child points BACK.
 7612. When attaching to other faces, if possible the child's UP anchor will point UP; otherwise, the BACK of the child points up (so the FRONT is pointed down).  
 762
 763To show how this works we use this prismoid where the blue arrow is
 764pointing to the front and the green arrow points up.  Also note that
 765the front left edge is the only right angle.  
 766
 767```openscad-3D
 768include <BOSL2/std.scad>
 769color_this("orange")
 770prismoid([8,8],[6,6],shift=-[1,1],h=8) {
 771     attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
 772     attach(FWD,BOT) anchor_arrow(s=12);     
 773}
 774```
 775
 776If we attach this to the TOP by the LEFT side then we get the result
 777below.  Notice how the green UP arrow is pointing back.
 778
 779```openscad-3D
 780include <BOSL2/std.scad>
 781cube(30) attach(TOP,LEFT)
 782color_this("orange")
 783  prismoid([8,8],[6,6],shift=-[1,1],h=8) {
 784    attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
 785    attach(FWD,BOT) anchor_arrow(s=12);     
 786  }
 787```
 788
 789If we attach to the RIGHT using the same LEFT side anchor on the
 790prismoid then we get the result below.  Note that the green UP anchor
 791is pointing UP, in accordance with rule 2 from above.  
 792
 793```openscad-3D
 794include <BOSL2/std.scad>
 795cube(30) attach(RIGHT,LEFT)
 796color_this("orange")
 797  prismoid([8,8],[6,6],shift=-[1,1],h=8) {
 798    attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
 799    attach(FWD,BOT) anchor_arrow(s=12);     
 800  }
 801```
 802
 803The green UP arrow can always be arranged to point up unless we attach
 804either the top or bottom to one of the cube's vertical faces.  Here we
 805attach the bottom so you can still see both arrows.  The blue FRONT
 806arrow on the object is pointing down, as expected based on rule 2.  
 807
 808```openscad-3D
 809include <BOSL2/std.scad>
 810cube(30) attach(RIGHT,BOT)
 811color_this("orange")
 812  prismoid([8,8],[6,6],shift=-[1,1],h=8) {
 813    attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
 814    attach(FWD,BOT) anchor_arrow(s=12);     
 815  }
 816```
 817
 818What do you do if the direction the child appears is not the direction
 819you need?  To address this issue `attach()` provides a `spin=`
 820parameter which spins the attached child around the axis defined by
 821the joined anchor vectors.  Here is the last example with a rotation
 822applied to bring the front anchor back to the front:
 823
 824```openscad-3D
 825include <BOSL2/std.scad>
 826cube(30) attach(RIGHT,BOT,spin=-90)
 827color_this("orange")
 828  prismoid([8,8],[6,6],shift=-[1,1],h=8) {
 829    attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
 830    attach(FWD,BOT) anchor_arrow(s=12);     
 831  }
 832```
 833
 834Be aware that specifying `spin=` to `attach()` is not equivalent to
 835using the `spin=` argument to the child.  Unlike `orient=` and
 836`anchor=`, which are ignored, the child `spin=` argument is still
 837respected, but it may be difficult to figure out which axis it will
 838rotate on.  It is more intuitive to ignore the child spin parameter
 839and only use the spin parameter to `attach()`.  The spin must be
 840scalar but need not be a multiple of 90 degrees.
 841
 842```openscad-3D
 843include <BOSL2/std.scad>
 844cube(30) attach(RIGHT,BOT,spin=-37)
 845color_this("orange")
 846  prismoid([8,8],[6,6],shift=-[1,1],h=8) {
 847    attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
 848    attach(FWD,BOT) anchor_arrow(s=12);     
 849  }
 850```
 851
 852By default, `attach()` places the child exactly flush with the surface
 853of the parent.  Sometimes it's useful to have the child overlap the
 854parent by translating it into the parent.  You can do this with the
 855`overlap=` argument to `attach()`.  A positive value will cause the
 856child to overlap the parent, and a negative value will move the child
 857away from the parent, leaving a small gap.  In the first example we use a very large value of
 858overlap so the cube is sunk deeply into the parent.  In the second
 859example a large negative overlap value raises the child high above the
 860parent.  
 861
 862```openscad-3D
 863include <BOSL2/std.scad>
 864cuboid(50)
 865    attach(TOP,BOT,overlap=15)
 866        color("green")cuboid(20);
 867```
 868
 869```openscad-3D
 870include <BOSL2/std.scad>
 871cube(50,center=true)
 872    attach(TOP,BOT,overlap=-20)
 873        cyl(d=20,h=20);
 874```
 875
 876Another feature provided by the double argument form of `attach()` is
 877alignment, which works in a similar way to `align()`.  You can specify
 878`align=` to align the attached child to an edge or corner.  The
 879example below shows five different alignments.  
 880
 881```openscad-3D;Big
 882include <BOSL2/std.scad>
 883module thing(){
 884  color_this("orange")
 885    prismoid([8,8],[6,6],shift=-[1,1],h=8) {
 886      attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
 887      attach(FWD,BOT) anchor_arrow(s=12);     
 888    }
 889}
 890prismoid([50,50],[35,35],h=25,anchor=BOT){
 891  attach(TOP,BOT,align=FRONT) thing();
 892  attach(RIGHT,BOT,align=BOT) thing();    
 893  attach(RIGHT,BACK,align=FRONT) thing();
 894  attach(FRONT,BACK,align=BOT,spin=45) thing();
 895  attach(TOP,RIGHT,align=RIGHT,spin=90) thing();
 896}
 897```
 898
 899As with `align()` if you turn an object 90 degrees it can match up
 900with parallel edges, but if you turn it an arbitrary angle, a corner
 901of the child will contact the edge of the parent.  Also like align()
 902the anchor points of the parent and child are aligned but this does
 903not necessarily mean that edges line up neatly when the shapes have
 904varying angles.  This misalignment is visible in the object attached
 905at the RIGHT and aligned to the FRONT.
 906
 907You may be wondering why all this fuss with align is necessary.
 908Couldn't you just attach an object at an anchor on an edge?  When you
 909do this, the object will be attached using the edge anchor, which is
 910not perpendicular to the faces of the object.  The example below shows
 911attachment to an edge anchor and also a corner anchor.  
 912
 913```openscad-3D
 914include <BOSL2/std.scad>
 915cube(30)
 916   color("orange"){
 917     attach(RIGHT+FRONT,BOT) 
 918        prismoid([8,8],[6,6],shift=-[1,1],h=8);
 919     attach(TOP+LEFT+FWD,BOT)
 920        prismoid([8,8],[6,6],shift=-[1,1],h=8);
 921   }
 922```
 923
 924When using the `align` option to `attach()` you can also set `inset`,
 925which works the same way as the `inset` parameter to `align()`.  It
 926shifts the child away from the edge or edges where it is aligned by
 927the specified amount.  
 928
 929```openscad-3D
 930include <BOSL2/std.scad>
 931prismoid([50,50],[50,25],25){
 932  attach(FWD,BOT,align=TOP,inset=3) color("lavender")cuboid(5);
 933  attach(FWD,BOT,align=BOT+RIGHT,inset=3) color("purple")cuboid(5);
 934}
 935```
 936
 937The last capability provided by `attach()` is to attach the child
 938**inside** the parent object.  This is useful if you want to subtract
 939the child from the parent.  Doing this requires using tagged
 940operations with `diff()` which is explained in more detail below. 
 941For the examples here, note that the `diff()` and `tag()` operations
 942that appear cause the child to be subtracted.  We return to the
 943example that started this section, with anchor arrows shown on the two
 944objects.  
 945
 946```openscad-3D
 947include <BOSL2/std.scad>
 948cube(50,anchor=BOT) attach(TOP) anchor_arrow(30);
 949right(60)cylinder(d1=30,d2=15,h=25) attach(TOP) anchor_arrow(30);
 950```
 951
 952Inside attachment is activated using `inside=true` and it lines up the
 953anchor arrows so they point together the **same** direction instead of
 954opposite directions like regular outside attachment.  The result in
 955this case is appears below, where we have cut away the front half to
 956show the interior: 
 957
 958```openscad-3D
 959include <BOSL2/std.scad>
 960back_half(s=200)
 961diff()
 962cube(50,anchor=BOT)
 963  attach(TOP,TOP,inside=true)
 964    cylinder(d1=30,d2=15,h=25);
 965```
 966
 967The top of the cavity has a thin layer on it, which occurs because the
 968two objects share a face in the difference.  To fix this you can use
 969the `shiftout` parameter to `attach()`.  In this case you could also
 970use a negative `overlay` value, but the `shiftout` parameter shifts
 971out in every direction that is needed, which may be three directions
 972if you align the child at a corner.  The above example looks like this
 973with with the shift added:
 974
 975```openscad-3D
 976include <BOSL2/std.scad>
 977back_half(s=200)
 978diff()
 979cube(50,anchor=BOT)
 980  attach(TOP,TOP,inside=true,shiftout=0.01)
 981    cylinder(d1=30,d2=15,h=25);
 982```
 983
 984Here is an example of connecting the same object on the right, but
 985this time with the BOTTOM anchor.  Note how the BOTTOM anchor is
 986aligned to the RIGHT so it is parallel and pointing in the same
 987direction as the RIGHT anchor.  
 988
 989```openscad-3D
 990include <BOSL2/std.scad>
 991back_half(s=200)
 992diff()
 993cuboid(50)
 994  attach(RIGHT,BOT,inside=true,shiftout=0.01)
 995    cylinder(d1=30,d2=15,h=25);
 996```
 997
 998Here is an example where alignment moves the object into the corner,
 999and we benefit from shiftout providing 3 dimensions of adjustment:
1000
1001```openscad-3D
1002include <BOSL2/std.scad>
1003diff()
1004cuboid(10)
1005  attach(TOP,TOP,align=RIGHT+FWD,inside=true,shiftout=.01)
1006    cuboid([2,5,9]);
1007```
1008
1009As with `position()`, with any use of `attach()` you can still apply your own translations and
1010other transformations even after attaching an object.  However, the
1011order of operations now matters.  If you apply a translation outside
1012of the anchor then it acts in the parent's global coordinate system, so the
1013child moves up in this example, where the light gray shows the
1014untranslated object.  
1015
1016```openscad-3D
1017include <BOSL2/std.scad>
1018cuboid(50){
1019  %attach(RIGHT,BOT)
1020    cyl(d1=30,d2=15,h=25);
1021  up(13)
1022    color("green") attach(RIGHT,BOT)
1023      cyl(d1=30,d2=15,h=25);
1024}
1025```
1026
1027On the other hand, if you put the translation between the attach and
1028the object in your code, then it will act in the local coordinate system of
1029the parent at the parent's anchor, so in the example below it moves to the right.  
1030
1031```openscad-3D
1032include <BOSL2/std.scad>
1033cuboid(50){
1034  %attach(RIGHT,BOT)
1035    cyl(d1=30,d2=15,h=25);
1036  color("green") attach(RIGHT,BOT)
1037    up(13)
1038      cyl(d1=30,d2=15,h=25);
1039}
1040```
1041
1042Parent-child Anchor attachment with CENTER anchors can be surprising because the anchors
1043both point upwards, so in the example below, the child's CENTER anchor
1044points up, so it is inverted when it is attached to the parent cone.
1045Note that the anchors are CENTER anchors, so the bases of the anchors are
1046hidden in the middle of the objects.  
1047
1048```openscad-3D
1049include <BOSL2/std.scad>
1050cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
1051right(40)cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
1052```
1053
1054```openscad-3D
1055include <BOSL2/std.scad>
1056cylinder(d1=30,d2=15,h=25)
1057    attach(CENTER,CENTER)
1058        cylinder(d1=30,d2=15,h=25);
1059```
1060
1061## Parent Anchor Attachment (Single Argument Attachment)
1062
1063The second form of attachment is parent anchor attachment, which just
1064uses a single argument.  This form of attachment is less useful in
1065general and does not provide alignment.  When you give `attach()` a parent anchor but no child anchor it
1066orients the child according to the parent anchor direction but then
1067simply places the child based on its internally defined anchor at the
1068parent anchor position.  For most objects the default anchor is the
1069CENTER anchor, so objects will appear sunk half-way into the parent.
1070
1071```openscad-3D
1072include <BOSL2/std.scad>
1073cuboid(30)
1074    attach(TOP)
1075        color("green")cuboid(10);
1076```
1077
1078Some objects such as `cylinder()`, `prismoid()`, and `anchor_arrow()` have default anchors on the bottom, so they will appear
1079on the surface.  For objects like this you can save a little bit of
1080typing by using parent anchor attachment.  But in the case of `cube()`
1081the anchor is not centered, so the result is:
1082
1083```openscad-3D
1084include <BOSL2/std.scad>
1085cube(30)
1086    attach(TOP)
1087        color("green")cube(10);
1088```
1089
1090In order to make single argument attachment produce the results you
1091need you will probably need to change the child anchor.  Note that unlike
1092parent-child anchor attachment, **with parent anchor attachment the `anchor=` and `orient=` arguments
1093are respected.**  We could therefore place a cuboid like this:
1094
1095```openscad-3D
1096include <BOSL2/std.scad>
1097cuboid(30)
1098  attach(RIGHT)
1099      color("green")cuboid(10,anchor=BOT);
1100```
1101
1102If you need to place a cuboid at the anchor point but need it anchored
1103relative to one of the bottom edge or corner anchors then you can do
1104that with parent anchor attachment:
1105
1106```openscad-3D
1107include <BOSL2/std.scad>
1108cuboid(30)
1109  attach(RIGHT)
1110      color("green")cuboid(10,anchor=BOT+FWD);
1111```
1112
1113Another case where single argument attachment is useful is when the
1114child doesn't have proper attachment support.
1115If you use double argument attachment in such cases the results will
1116be incorrect because the child doesn't properly respond to the
1117internally propagated anchor directives.  With single argument
1118attachment, this is not a problem: the origin
1119of the child will be placed at the parent anchor point.  One module
1120without attachment support is `linear_extrude()`.  
1121
1122```openscad-3D
1123include <BOSL2/std.scad>
1124cuboid(20)
1125  attach(RIGHT)
1126     color("red")linear_extrude(height=2) star(n=7,ir=3,or=7);
1127```
1128
1129As noted earlier, you can set `orient=` for children with parent
1130anchor attachment, though the behavior may not be intuitive because
1131the attachment process transforms the coordinate system and the
1132orientation is done in the attached coordinate system.  It may be
1133helpful to start with the object attached to TOP and recall the rules
1134from the previous section about how orientation works.  The same rules
1135apply here.  Note that the forward arrow is pointing down after
1136attaching the object on the RIGHT face.
1137
1138```openscad-3D
1139include <BOSL2/std.scad>
1140cuboid(20){
1141  attach(RIGHT)
1142     color_this("red")cuboid([2,4,8],orient=RIGHT,anchor=RIGHT)
1143        attach(FWD) anchor_arrow();
1144  attach(TOP)
1145     color_this("red")cuboid([2,4,8],orient=RIGHT,anchor=RIGHT)
1146            attach(FWD) anchor_arrow();
1147}
1148```
1149
1150
1151
1152## Positioning and Attaching Multiple Children
1153
1154You can attach or position more than one child at a time by enclosing them all in braces:
1155
1156```openscad-3D
1157include <BOSL2/std.scad>
1158cube(50, center=true) {
1159    attach(TOP) cylinder(d1=50,d2=20,h=20);
1160    position(RIGHT) cylinder(d1=50,d2=20,h=20);
1161}
1162```
1163
1164If you want to attach the same shape to multiple places on the same parent, you can pass the
1165desired anchors as a list to the `attach()` or `position()` modules:
1166
1167```openscad-3D
1168include <BOSL2/std.scad>
1169cube(50, center=true)
1170    attach([RIGHT,FRONT],TOP) cylinder(d1=35,d2=20,h=25);
1171```
1172
1173```openscad-3D
1174include <BOSL2/std.scad>
1175cube(50, center=true)
1176    position([TOP,RIGHT,FRONT]) cylinder(d1=35,d2=20,h=25);
1177```
1178
1179
1180## Attaching 2D Children
1181You can use attachments in 2D as well.  As usual for the 2D case you
1182can use TOP and BOTTOM as alternative to BACK and FORWARD.  With
1183parent-child anchor attachment you cannot use the spin parameter to
1184`attach()` nor can you specify spin to the child.  Spinning the child
1185on the Z axis would rotate the anchor arrows out of alignment.  
1186
1187```openscad-2D
1188include <BOSL2/std.scad>
1189rect(50){
1190    attach(RIGHT,FRONT)
1191        color("red")trapezoid(w1=30,w2=0,h=30);
1192    attach(LEFT,FRONT,align=[FRONT,BACK],inset=3)
1193        color("green") trapezoid(w1=25, w2=0,h=30);
1194}
1195```
1196
1197```openscad-2D
1198include <BOSL2/std.scad>
1199diff()
1200circle(d=50){
1201    attach(TOP,BOT,overlap=5)
1202        trapezoid(w1=30,w2=0,h=30);
1203    attach(BOT,BOT,inside=true)
1204        tag("remove")
1205        trapezoid(w1=30,w2=0,h=30);
1206}        
1207```
1208
1209
1210## Tagged Operations
1211BOSL2 introduces the concept of tags.  Tags are names that can be given to attachables, so that
1212you can refer to them when performing `diff()`, `intersect()`, and `conv_hull()` operations.
1213Each object can have no more than one tag at a time.  
1214
1215### `diff([remove], [keep])`
1216The `diff()` operator is used to difference away all shapes marked with the tag(s) given to
1217`remove`, from the other shapes.  
1218
1219For example, to difference away a child cylinder from the middle of a parent cube, you can
1220do this:
1221
1222```openscad-3D
1223include <BOSL2/std.scad>
1224diff("hole")
1225cube(100, center=true)
1226    tag("hole")cylinder(h=101, d=50, center=true);
1227```
1228
1229The `keep=` argument takes tags for shapes that you want to keep in the output.
1230
1231```openscad-3D
1232include <BOSL2/std.scad>
1233diff("dish", keep="antenna")
1234cube(100, center=true)
1235    attach([FRONT,TOP], overlap=33) {
1236        tag("dish") cylinder(h=33.1, d1=0, d2=95);
1237        tag("antenna") cylinder(h=33.1, d=10);
1238    }
1239```
1240
1241Remember that tags are inherited by children.  In this case, we need to explicitly
1242untag the first cylinder (or change its tag to something else), or it
1243will inherit the "keep" tag and get kept.  
1244
1245```openscad-3D
1246include <BOSL2/std.scad>
1247diff("hole", "keep")
1248tag("keep")cube(100, center=true)
1249    attach([RIGHT,TOP]) {
1250        tag("") cylinder(d=95, h=5);
1251        tag("hole") cylinder(d=50, h=11, anchor=CTR);
1252    }
1253```
1254
1255
1256You can of course apply `tag()` to several children.
1257
1258```openscad-3D
1259include <BOSL2/std.scad>
1260diff("hole")
1261cube(100, center=true)
1262    attach([FRONT,TOP], overlap=20)
1263        tag("hole") {
1264            cylinder(h=20.1, d1=0, d2=95);
1265            down(10) cylinder(h=30, d=30);
1266        }
1267```
1268
1269Many of the modules that use tags have default values for their tags.  For diff the default
1270remove tag is "remove" and the default keep tag is "keep".  In this example we rely on the
1271default values:
1272
1273```openscad-3D
1274include <BOSL2/std.scad>
1275diff()
1276sphere(d=100) {
1277    tag("keep")xcyl(d=40, l=120);
1278    tag("remove")cuboid([40,120,100]);
1279}
1280```
1281
1282
1283The parent object can be differenced away from other shapes.  Tags are inherited by children,
1284though, so you will need to set the tags of the children as well as the parent.
1285
1286```openscad-3D
1287include <BOSL2/std.scad>
1288diff("hole")
1289tag("hole")cube([20,11,45], center=true)
1290    tag("body")cube([40,10,90], center=true);
1291```
1292
1293Tags (and therefore tag-based operations like `diff()`) only work correctly with attachable
1294children.  However, a number of built-in modules for making shapes are *not* attachable.
1295Some notable non-attachable modules are `text()`, `linear_extrude()`, `rotate_extrude()`,
1296`polygon()`, `polyhedron()`, `import()`, `surface()`, `union()`, `difference()`,
1297`intersection()`, `offset()`, `hull()`, and `minkowski()`.
1298
1299To allow you to use tags-based operations with non-attachable shapes, you can wrap them with the
1300`force_tag()` module to specify their tags.  For example:
1301
1302```openscad-3D
1303include <BOSL2/std.scad>
1304diff("hole")
1305cuboid(50)
1306  attach(TOP)
1307    force_tag("hole")
1308      rotate_extrude()
1309        right(15)
1310          square(10,center=true);
1311```
1312
1313### `intersect([intersect], [keep])`
1314
1315To perform an intersection of attachables, you can use the `intersect()` module.  This is
1316specifically intended to address the situation where you want intersections involving a parent
1317and a child, something that is impossible with the native `intersection()` module.  This module
1318treats the children in three groups: objects matching the `intersect` tags, objects matching
1319the tags listed in `keep` and the remaining objects that don't match any listed tags.  The
1320intersection is computed between the union of the `intersect` tagged objects and the union of
1321the objects that don't match any listed tags.  Finally the objects listed in `keep` are union
1322ed with the result.  
1323
1324In this example the parent is intersected with a conical bounding shape.  
1325
1326```openscad-3D
1327include <BOSL2/std.scad>
1328intersect("bounds")
1329cube(100, center=true)
1330    tag("bounds") cylinder(h=100, d1=120, d2=95, center=true, $fn=72);
1331```
1332
1333In this example the child objects are intersected with the bounding box parent.  
1334
1335```openscad-3D
1336include <BOSL2/std.scad>
1337intersect("pole cap")
1338cube(100, center=true)
1339    attach([TOP,RIGHT]) {
1340        tag("pole")cube([40,40,80],center=true);
1341        tag("cap")sphere(d=40*sqrt(2));
1342    }
1343```
1344
1345The default `intersect` tag is "intersect" and the default `keep` tag is "keep".  Here is an
1346example where "keep" is used to keep the pole from being removed by the intersection. 
1347
1348```openscad-3D
1349include <BOSL2/std.scad>
1350intersect()
1351cube(100, center=true) {
1352    tag("intersect")cylinder(h=100, d1=120, d2=95, center=true, $fn=72);
1353    tag("keep")zrot(45) xcyl(h=140, d=20, $fn=36);
1354}
1355```
1356
1357### `conv_hull([keep])`
1358You can use the `conv_hull()` module to hull shapes together.  Objects
1359marked with the keep tags are excluded from the hull and unioned into the final result.
1360The default keep tag is "keep".  
1361
1362
1363```openscad-3D
1364include <BOSL2/std.scad>
1365conv_hull()
1366cube(50, center=true) {
1367    cyl(h=100, d=20);
1368    tag("keep")xcyl(h=100, d=20);
1369}
1370```
1371
1372
1373## 3D Masking Attachments
1374To make it easier to mask away shapes from various edges of an attachable parent shape, there
1375are a few specialized alternatives to the `attach()` and `position()` modules.
1376
1377### `edge_mask()`
1378If you have a 3D mask shape that you want to difference away from various edges, you can use
1379the `edge_mask()` module.  This module will take a vertically oriented shape, and will rotate
1380and move it such that the BACK, RIGHT (X+,Y+) side of the shape will be aligned with the given
1381edges.  The shape will be tagged as a "remove" so that you can use
1382`diff()` with its default "remove" tag.  For example,
1383here's a shape for rounding an edge:
1384
1385```openscad-3D
1386include <BOSL2/std.scad>
1387module round_edge(l,r) difference() {
1388    translate([-1,-1,-l/2])
1389        cube([r+1,r+1,l]);
1390    translate([r,r])
1391        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1392}
1393round_edge(l=30, r=19);
1394```
1395
1396You can use that mask to round various edges of a cube:
1397
1398```openscad-3D
1399include <BOSL2/std.scad>
1400module round_edge(l,r) difference() {
1401    translate([-1,-1,-l/2])
1402        cube([r+1,r+1,l]);
1403    translate([r,r])
1404        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1405}
1406diff()
1407cube([50,60,70],center=true)
1408    edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT])
1409        round_edge(l=71,r=10);
1410```
1411
1412### `corner_mask()`
1413If you have a 3D mask shape that you want to difference away from various corners, you can use
1414the `corner_mask()` module.  This module will take a shape and rotate and move it such that the
1415BACK RIGHT TOP (X+,Y+,Z+) side of the shape will be aligned with the given corner.  The shape
1416will be tagged as a "remove" so that you can use `diff()` with its
1417default "remove" tag.  For example, here's a shape for
1418rounding a corner:
1419
1420```openscad-3D
1421include <BOSL2/std.scad>
1422module round_corner(r) difference() {
1423    translate(-[1,1,1])
1424        cube(r+1);
1425    translate([r,r,r])
1426        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1427}
1428round_corner(r=10);
1429```
1430
1431You can use that mask to round various corners of a cube:
1432
1433```openscad-3D
1434include <BOSL2/std.scad>
1435module round_corner(r) difference() {
1436    translate(-[1,1,1])
1437        cube(r+1);
1438    translate([r,r,r])
1439        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1440}
1441diff()
1442cube([50,60,70],center=true)
1443    corner_mask([TOP,FRONT],LEFT+FRONT+TOP)
1444        round_corner(r=10);
1445```
1446
1447### Mix and Match Masks
1448You can use `edge_mask()` and `corner_mask()` together as well:
1449
1450```openscad-3D
1451include <BOSL2/std.scad>
1452module round_corner(r) difference() {
1453    translate(-[1,1,1])
1454        cube(r+1);
1455    translate([r,r,r])
1456        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1457}
1458module round_edge(l,r) difference() {
1459    translate([-1,-1,-l/2])
1460        cube([r+1,r+1,l]);
1461    translate([r,r])
1462        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1463}
1464diff()
1465cube([50,60,70],center=true) {
1466    edge_mask("ALL") round_edge(l=71,r=10);
1467    corner_mask("ALL") round_corner(r=10);
1468}
1469```
1470
1471## 2D Profile Mask Attachments
1472While 3D mask shapes give you a great deal of control, you need to make sure they are correctly
1473sized, and you need to provide separate mask shapes for corners and edges.  Often, a single 2D
1474profile could be used to describe the edge mask shape (via `linear_extrude()`), and the corner
1475mask shape (via `rotate_extrude()`).  This is where `edge_profile()`, `corner_profile()`, and
1476`face_profile()` come in.
1477
1478### `edge_profile()`
1479Using the `edge_profile()` module, you can provide a 2D profile shape and it will be linearly
1480extruded to a mask of the appropriate length for each given edge.  The resultant mask will be
1481tagged with "remove" so that you can difference it away with `diff()`
1482with the default "remove" tag.  The 2D profile is
1483assumed to be oriented with the BACK, RIGHT (X+,Y+) quadrant as the "cutter edge" that gets
1484re-oriented towards the edges of the parent shape.  A typical mask profile for chamfering an
1485edge may look like:
1486
1487```openscad-2D
1488include <BOSL2/std.scad>
1489mask2d_roundover(10);
1490```
1491
1492Using that mask profile, you can mask the edges of a cube like:
1493
1494```openscad-3D
1495include <BOSL2/std.scad>
1496diff()
1497cube([50,60,70],center=true)
1498   edge_profile("ALL")
1499       mask2d_roundover(10);
1500```
1501
1502### `corner_profile()`
1503You can use the same profile to make a rounded corner mask as well:
1504
1505```openscad-3D
1506include <BOSL2/std.scad>
1507diff()
1508cube([50,60,70],center=true)
1509   corner_profile("ALL", r=10)
1510       mask2d_roundover(10);
1511```
1512
1513### `face_profile()`
1514As a simple shortcut to apply a profile mask to all edges and corners of a face, you can use the
1515`face_profile()` module:
1516
1517```openscad-3D
1518include <BOSL2/std.scad>
1519diff()
1520cube([50,60,70],center=true)
1521   face_profile(TOP, r=10)
1522       mask2d_roundover(10);
1523```
1524
1525
1526## Coloring Attachables
1527Usually, when coloring a shape with the `color()` module, the parent color overrides the colors of
1528all children.  This is often not what you want:
1529
1530```openscad-3D
1531include <BOSL2/std.scad>
1532$fn = 24;
1533color("red") spheroid(d=3) {
1534    attach(CENTER,BOT) color("white") cyl(h=10, d=1) {
1535        attach(TOP,BOT) color("green") cyl(h=5, d1=3, d2=0);
1536    }
1537}
1538```
1539
1540If you use the `recolor()` module, however, the child's color
1541overrides the color of the parent.  This is probably easier to understand by example:
1542
1543```openscad-3D
1544include <BOSL2/std.scad>
1545$fn = 24;
1546recolor("red") spheroid(d=3) {
1547    attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) {
1548        attach(TOP,BOT) recolor("green") cyl(h=5, d1=3, d2=0);
1549    }
1550}
1551```
1552
1553Be aware that `recolor()` will only work if you avoid using the native
1554`color()` module.  Also note that `recolor()` still affects all its
1555children.  If you want to color an object without affecting the
1556children you can use `color_this()`.  See the difference below:
1557
1558```openscad-3D
1559include <BOSL2/std.scad>
1560$fn = 24;
1561recolor("red") spheroid(d=3) {
1562    attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) {
1563        attach(TOP,BOT)  cyl(h=5, d1=3, d2=0);
1564    }
1565}
1566right(5)
1567recolor("red") spheroid(d=3) {
1568    attach(CENTER,BOT) color_this("white") cyl(h=10, d=1) {
1569        attach(TOP,BOT)  cyl(h=5, d1=3, d2=0);
1570    }
1571}
1572```
1573
1574As with all of the attachable features, these color modules only work
1575on attachable objects, so they will have no effect on objects you
1576create using `linear_extrude()` or `rotate_extrude()`.  
1577
1578
1579## Making Attachables
1580To make a shape attachable, you just need to wrap it with an `attachable()` module with a
1581basic description of the shape's geometry.  By default, the shape is expected to be centered
1582at the origin.  The `attachable()` module expects exactly two children.  The first will be
1583the shape to make attachable, and the second will be `children()`,
1584literally.
1585
1586### Pass-through Attachables
1587The simplest way to make your own attachable module is to simply pass
1588through to a pre-existing attachable submodule.  This could be
1589appropriate if you want to rename a module, or if the anchors of an
1590existing module are suited to (or good enough for) your object.  In
1591order for your attachable module to work properly you need to accept
1592the `anchor`, `spin` and `orient` parameters, give them suitable
1593defaults, and pass them to the attachable submodule.  Don't forget to
1594pass the children to the attachable submodule as well, or your new
1595module will ignore its children.  
1596
1597```openscad-3D
1598include <BOSL2/std.scad>
1599$fn=32;
1600module cutcube(anchor=CENTER,spin=0,orient=UP)
1601{
1602   tag_scope(){
1603     diff()
1604       cuboid(15, rounding=2, anchor=anchor,spin=spin,orient=orient){
1605         tag("remove")attach(TOP)cuboid(5);
1606         children();
1607       }
1608   }
1609}
1610diff()
1611cutcube()
1612  tag("remove")attach(RIGHT) cyl(d=2,h=8);
1613```
1614
1615### Prismoidal/Cuboidal Attachables
1616To make a cuboidal or prismoidal shape attachable, you use the `size`, `size2`, and `offset`
1617arguments of `attachable()`.
1618
1619In the most basic form, where the shape is fully cuboid, with top and bottom of the same size,
1620and directly over one another, you can just use `size=`.
1621
1622```openscad-3D
1623include <BOSL2/std.scad>
1624module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
1625    attachable(anchor,spin,orient, size=[s*3,s,s]) {
1626        union() {
1627            xcopies(2*s) cube(s, center=true);
1628            xcyl(h=2*s, d=s/4);
1629        }
1630        children();
1631    }
1632}
1633cubic_barbell(100) show_anchors(60);
1634```
1635
1636When the shape is prismoidal, where the top is a different size from the bottom, you can use
1637the `size2=` argument as well. While `size=` takes all three axes sizes, the `size2=` argument
1638only takes the [X,Y] sizes of the top of the shape.
1639
1640```openscad-3D
1641include <BOSL2/std.scad>
1642module prismoidal(size=[100,100,100], scale=0.5, anchor=CENTER, spin=0, orient=UP) {
1643    attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale) {
1644        hull() {
1645            up(size.z/2-0.005)
1646                linear_extrude(height=0.01, center=true)
1647                    square([size.x,size.y]*scale, center=true);
1648            down(size.z/2-0.005)
1649                linear_extrude(height=0.01, center=true)
1650                    square([size.x,size.y], center=true);
1651        }
1652        children();
1653    }
1654}
1655prismoidal([100,60,30], scale=0.5) show_anchors(20);
1656```
1657
1658When the top of the prismoid can be shifted away from directly above the bottom, you can use
1659the `shift=` argument.  The `shift=` argument takes an [X,Y] vector of the offset of the center
1660of the top from the XY center of the bottom of the shape.
1661
1662```openscad-3D
1663include <BOSL2/std.scad>
1664module prismoidal(size=[100,100,100], scale=0.5, shift=[0,0], anchor=CENTER, spin=0, orient=UP) {
1665    attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale, shift=shift) {
1666        hull() {
1667            translate([shift.x, shift.y, size.z/2-0.005])
1668                linear_extrude(height=0.01, center=true)
1669                    square([size.x,size.y]*scale, center=true);
1670            down(size.z/2-0.005)
1671                linear_extrude(height=0.01, center=true)
1672                    square([size.x,size.y], center=true);
1673        }
1674        children();
1675    }
1676}
1677prismoidal([100,60,30], scale=0.5, shift=[-30,20]) show_anchors(20);
1678```
1679
1680In the case that the prismoid is not oriented vertically, (ie, where the `shift=` or `size2=`
1681arguments should refer to a plane other than XY) you can use the `axis=` argument.  This lets
1682you make prismoids naturally oriented forwards/backwards or sideways.
1683
1684```openscad-3D
1685include <BOSL2/std.scad>
1686module yprismoidal(
1687    size=[100,100,100], scale=0.5, shift=[0,0],
1688    anchor=CENTER, spin=0, orient=UP
1689) {
1690    attachable(
1691        anchor, spin, orient,
1692        size=size, size2=point2d(size)*scale,
1693        shift=shift, axis=BACK
1694    ) {
1695        xrot(-90) hull() {
1696            translate([shift.x, shift.y, size.z/2-0.005])
1697                linear_extrude(height=0.01, center=true)
1698                    square([size.x,size.y]*scale, center=true);
1699            down(size.z/2-0.005)
1700                linear_extrude(height=0.01, center=true)
1701                    square([size.x,size.y], center=true);
1702        }
1703        children();
1704    }
1705}
1706yprismoidal([100,60,30], scale=1.5, shift=[20,20]) show_anchors(20);
1707```
1708
1709
1710### Cylindrical Attachables
1711To make a cylindrical shape attachable, you use the `l`, and `r`/`d`, args of `attachable()`.
1712
1713```openscad-3D
1714include <BOSL2/std.scad>
1715module twistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
1716    r = get_radius(r=r,d=d,dflt=1);
1717    attachable(anchor,spin,orient, r=r, l=l) {
1718        linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
1719            star(n=20, r=r, ir=r*0.9);
1720        children();
1721    }
1722}
1723twistar(l=100, r=40) show_anchors(20);
1724```
1725
1726If the cylinder is elipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vector
1727to the `r=` or `d=` argument.
1728
1729```openscad-3D
1730include <BOSL2/std.scad>
1731module ovalstar(l,rx,ry, anchor=CENTER, spin=0, orient=UP) {
1732    attachable(anchor,spin,orient, r=[rx,ry], l=l) {
1733        linear_extrude(height=l, center=true, convexity=4)
1734            scale([1,ry/rx,1])
1735                star(n=20, r=rx, ir=rx*0.9);
1736        children();
1737    }
1738}
1739ovalstar(l=100, rx=50, ry=30) show_anchors(20);
1740```
1741
1742For cylindrical shapes that aren't oriented vertically, use the `axis=` argument.
1743
1744```openscad-3D
1745include <BOSL2/std.scad>
1746module ytwistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
1747    r = get_radius(r=r,d=d,dflt=1);
1748    attachable(anchor,spin,orient, r=r, l=l, axis=BACK) {
1749        xrot(-90)
1750            linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
1751                star(n=20, r=r, ir=r*0.9);
1752        children();
1753    }
1754}
1755ytwistar(l=100, r=40) show_anchors(20);
1756```
1757
1758### Conical Attachables
1759To make a conical shape attachable, you use the `l`, `r1`/`d1`, and `r2`/`d2`, args of
1760`attachable()`.
1761
1762```openscad-3D
1763include <BOSL2/std.scad>
1764module twistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
1765    r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
1766    r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
1767    attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
1768        linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
1769            star(n=20, r=r1, ir=r1*0.9);
1770        children();
1771    }
1772}
1773twistar(l=100, r1=40, r2=20) show_anchors(20);
1774```
1775
1776If the cone is ellipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vectors
1777to the `r1=`/`r2=` or `d1=`/`d2=` arguments.
1778
1779```openscad-3D
1780include <BOSL2/std.scad>
1781module ovalish(l,rx1,ry1,rx2,ry2, anchor=CENTER, spin=0, orient=UP) {
1782    attachable(anchor,spin,orient, r1=[rx1,ry1], r2=[rx2,ry2], l=l) {
1783        hull() {
1784            up(l/2-0.005)
1785                linear_extrude(height=0.01, center=true)
1786                    ellipse([rx2,ry2]);
1787            down(l/2-0.005)
1788                linear_extrude(height=0.01, center=true)
1789                    ellipse([rx1,ry1]);
1790        }
1791        children();
1792    }
1793}
1794ovalish(l=100, rx1=50, ry1=30, rx2=30, ry2=50) show_anchors(20);
1795```
1796
1797For conical shapes that are not oriented vertically, use the `axis=` argument to indicate the
1798direction of the primary shape axis:
1799
1800```openscad-3D
1801include <BOSL2/std.scad>
1802module ytwistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
1803    r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
1804    r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
1805    attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK) {
1806        xrot(-90)
1807            linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
1808                star(n=20, r=r1, ir=r1*0.9);
1809        children();
1810    }
1811}
1812ytwistar(l=100, r1=40, r2=20) show_anchors(20);
1813```
1814
1815### Spherical Attachables
1816To make a spherical shape attachable, you use the `r`/`d` args of `attachable()`.
1817
1818```openscad-3D
1819include <BOSL2/std.scad>
1820module spikeball(r, d, anchor=CENTER, spin=0, orient=UP) {
1821    r = get_radius(r=r,d=d,dflt=1);
1822    attachable(anchor,spin,orient, r=r*1.1) {
1823        union() {
1824            sphere_copies(r=r, n=512, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
1825            sphere(r=r);
1826        }
1827        children();
1828    }
1829}
1830spikeball(r=50) show_anchors(20);
1831```
1832
1833If the shape is an ellipsoid, you can pass a 3-item vector of sizes to `r=` or `d=`.
1834
1835```openscad-3D
1836include <BOSL2/std.scad>
1837module spikeball(r, d, scale, anchor=CENTER, spin=0, orient=UP) {
1838    r = get_radius(r=r,d=d,dflt=1);
1839    attachable(anchor,spin,orient, r=r*1.1*scale) {
1840        union() {
1841            sphere_copies(r=r, n=512, scale=scale, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
1842            scale(scale) sphere(r=r);
1843        }
1844        children();
1845    }
1846}
1847spikeball(r=50, scale=[0.75,1,1.5]) show_anchors(20);
1848```
1849
1850### VNF Attachables
1851If the shape just doesn't fit into any of the above categories, and you constructed it as a
1852[VNF](vnf.scad), you can use the VNF itself to describe the geometry with the `vnf=` argument.
1853
1854There are two variations to how anchoring can work for VNFs. When `extent=true`, (the default)
1855then a plane is projected out from the origin, perpendicularly in the direction of the anchor,
1856to the furthest distance that intersects with the VNF shape.  The anchor point is then the
1857center of the points that still intersect that plane.
1858
1859```openscad-FlatSpin,VPD=500
1860include <BOSL2/std.scad>
1861module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
1862    s2 = 3 * s;
1863    verts = [
1864        [0,0,-s2*sqrt(2)/2],
1865        each down(s/2, p=path3d(square(s,center=true))),
1866        each zrot(45, p=path3d(square(s2,center=true))),
1867        each up(s/2, p=path3d(square(s,center=true))),
1868        [0,0,s2*sqrt(2)/2]
1869    ];
1870    faces = [
1871        [0,2,1], [0,3,2], [0,4,3], [0,1,4],
1872        [1,2,6], [1,6,9], [6,10,9], [2,10,6],
1873        [1,5,4], [1,9,5], [9,12,5], [5,12,4],
1874        [4,8,3], [4,12,8], [12,11,8], [11,3,8],
1875        [2,3,7], [3,11,7], [7,11,10], [2,7,10],
1876        [9,10,13], [10,11,13], [11,12,13], [12,9,13]
1877    ];
1878    vnf = [verts, faces];
1879    attachable(anchor,spin,orient, vnf=vnf) {
1880        vnf_polyhedron(vnf);
1881        children();
1882    }
1883}
1884stellate_cube(25) {
1885    attach(UP+RIGHT) {
1886        anchor_arrow(20);
1887        %cube([100,100,0.1],center=true);
1888    }
1889}
1890```
1891
1892When `extent=false`, then the anchor point will be the furthest intersection of the VNF with
1893the anchor ray from the origin. The orientation of the anchor point will be the normal of the
1894face at the intersection.  If the intersection is at an edge or corner, then the orientation
1895will bisect the angles between the faces.
1896
1897```openscad-VPD=1250
1898include <BOSL2/std.scad>
1899module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
1900    s2 = 3 * s;
1901    verts = [
1902        [0,0,-s2*sqrt(2)/2],
1903        each down(s/2, p=path3d(square(s,center=true))),
1904        each zrot(45, p=path3d(square(s2,center=true))),
1905        each up(s/2, p=path3d(square(s,center=true))),
1906        [0,0,s2*sqrt(2)/2]
1907    ];
1908    faces = [
1909        [0,2,1], [0,3,2], [0,4,3], [0,1,4],
1910        [1,2,6], [1,6,9], [6,10,9], [2,10,6],
1911        [1,5,4], [1,9,5], [9,12,5], [5,12,4],
1912        [4,8,3], [4,12,8], [12,11,8], [11,3,8],
1913        [2,3,7], [3,11,7], [7,11,10], [2,7,10],
1914        [9,10,13], [10,11,13], [11,12,13], [12,9,13]
1915    ];
1916    vnf = [verts, faces];
1917    attachable(anchor,spin,orient, vnf=vnf, extent=false) {
1918        vnf_polyhedron(vnf);
1919        children();
1920    }
1921}
1922stellate_cube() show_anchors(50);
1923```
1924
1925```openscad-3D
1926include <BOSL2/std.scad>
1927$fn=32;
1928R = difference(circle(10), right(2, circle(9)));
1929linear_sweep(R,height=10,atype="hull")
1930    attach(RIGHT) anchor_arrow();
1931```
1932
1933
1934## Making Named Anchors
1935While vector anchors are often useful, sometimes there are logically extra attachment points that
1936aren't on the perimeter of the shape.  This is what named string anchors are for.  For example,
1937the `teardrop()` shape uses a cylindrical geometry for it's vector anchors, but it also provides
1938a named anchor "cap" that is at the tip of the hat of the teardrop shape.
1939
1940Named anchors are passed as an array of `named_anchor()`s to the `anchors=` argument of `attachable()`.
1941The `named_anchor()` call takes a name string, a positional point, an orientation vector, and a spin.
1942The name is the name of the anchor.  The positional point is where the anchor point is at.  The
1943orientation vector is the direction that a child attached at that anchor point should be oriented.
1944The spin is the number of degrees that an attached child should be rotated counter-clockwise around
1945the orientation vector.  Spin is optional, and defaults to 0.
1946
1947To make a simple attachable shape similar to a `teardrop()` that provides a "cap" anchor, you may
1948define it like this:
1949
1950```openscad-3D
1951include <BOSL2/std.scad>
1952module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
1953    anchors = [
1954        named_anchor("cap", [0,r/sin(45),0], BACK, 0)
1955    ];
1956    attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
1957        linear_extrude(height=thick, center=true) {
1958            circle(r=r);
1959            back(r*sin(45)) zrot(45) square(r, center=true);
1960        }
1961        children();
1962    }
1963}
1964raindrop(r=25, thick=20, anchor="cap");
1965```
1966
1967If you want multiple named anchors, just add them to the list of anchors:
1968
1969```openscad-FlatSpin,VPD=150
1970include <BOSL2/std.scad>
1971module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
1972    anchors = [
1973        named_anchor("captop", [0,r/sin(45), thick/2], BACK+UP,   0),
1974        named_anchor("cap",    [0,r/sin(45), 0      ], BACK,      0),
1975        named_anchor("capbot", [0,r/sin(45),-thick/2], BACK+DOWN, 0)
1976    ];
1977    attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
1978        linear_extrude(height=thick, center=true) {
1979            circle(r=r);
1980            back(r*sin(45)) zrot(45) square(r, center=true);
1981        }
1982        children();
1983    }
1984}
1985raindrop(r=15, thick=10) show_anchors();
1986```
1987
1988Sometimes the named anchor you want to add may be at a point that is reached through a complicated
1989set of translations and rotations.  One quick way to calculate that point is to reproduce those
1990transformations in a transformation matrix chain.  This is simplified by how you can use the
1991function forms of almost all the transformation modules to get the transformation matrices, and
1992chain them together with matrix multiplication.  For example, if you have:
1993
1994```
1995scale([1.1, 1.2, 1.3]) xrot(15) zrot(25) right(20) sphere(d=1);
1996```
1997
1998and you want to calculate the center point of the sphere, you can do it like:
1999
2000```
2001sphere_pt = apply(
2002    scale([1.1, 1.2, 1.3]) * xrot(15) * zrot(25) * right(20),
2003    [0,0,0]
2004);
2005```
2006
2007
2008## Overriding Standard Anchors
2009
2010Sometimes you may want to use the standard anchors but override some
2011of them.  Returning to the square barebell example above, the anchors
2012at the right and left sides are on the cubes at each end, but the
2013anchors at x=0 are in floating in space.  For prismoidal/cubic anchors
2014in 3D and trapezoidal/rectangular anchors in 2D we can override a single anchor by
2015specifying the override option and giving the anchor that is being
2016overridden, and then the replacement in the form
2017`[position, direction, spin]`.  Most often you will only want to
2018override the position.  If you omit the other list items then the
2019value drived from the standard anchor will be used. Below we override
2020position of the FWD anchor:
2021
2022```
2023module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
2024    override = [
2025                 [FWD,  [[0,-s/8,0]]]
2026               ];
2027    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
2028        union() {
2029            xcopies(2*s) cube(s, center=true);
2030            xcyl(h=2*s, d=s/4);
2031        }
2032        children();
2033    }
2034}
2035cubic_barbell(100) show_anchors(60);
2036```
2037
2038Note how the FWD anchor is now rooted on the cylindrical portion.  If
2039you wanted to also change its direction and spin you could do it like
2040this:
2041
2042```
2043module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
2044    override = [
2045                 [FWD,  [[0,-s/8,0], FWD+LEFT, 225]]
2046               ];
2047    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
2048        union() {
2049            xcopies(2*s) cube(s, center=true);
2050            xcyl(h=2*s, d=s/4);
2051        }
2052        children();
2053    }
2054}
2055cubic_barbell(100) show_anchors(60);
2056```
2057
2058In the above example we give three values for the override.  As
2059before, the first one places the anchor on the cylinder.  We have
2060added the second entry which points the anchor off to the left.
2061The third entry gives a spin override, whose effect is shown by the
2062position of the red flag on the arrow.  If you want to override all of
2063the x=0 anchors to be on the cylinder, with their standard directions,
2064you can do that by supplying a list: 
2065```
2066module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
2067    override = [
2068                 for(j=[-1:1:1], k=[-1:1:1])
2069                   if ([j,k]!=[0,0]) [[0,j,k], [s/8*unit([0,j,k])]]
2070               ];
2071    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
2072        union() {
2073            xcopies(2*s) cube(s, center=true);
2074            xcyl(h=2*s, d=s/4);
2075        }
2076        children();
2077    }
2078}
2079cubic_barbell(100) show_anchors(30);
2080```
2081
2082Now all of the anchors in the middle are all rooted to the cylinder.  Another
2083way to do the same thing is to use a function literal for override.
2084It will be called with the anchor as its argument and needs to return undef to just use
2085the default, or a `[position, direction, spin]` triple to override the
2086default.  As before, you can omit values to keep their default.
2087Here is the same example using a function literal for the override:
2088
2089```
2090module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
2091    override = function (anchor) 
2092          anchor.x!=0 || anchor==CTR ? undef  // Keep these
2093        : [s/8*unit(anchor)];
2094    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
2095        union() {
2096            xcopies(2*s) cube(s, center=true);
2097            xcyl(h=2*s, d=s/4);
2098        }
2099        children();
2100    }
2101}
2102cubic_barbell(100) show_anchors(30);
2103```